package com.smartum.mpr.services;

import java.io.IOException;

import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

import com.smartum.mpr.dao.ClientDao;
import com.smartum.mpr.dao.UserDao;
import com.smartum.mpr.exception.AlreadyClassyException;
import com.smartum.mpr.exception.AlreadyExistsUserException;
import com.smartum.mpr.exception.AlreadyGangsterException;
import com.smartum.mpr.exception.AlreadyHoaxException;
import com.smartum.mpr.exception.AlreadyLikedException;
import com.smartum.mpr.exception.AlreadyOddException;
import com.smartum.mpr.exception.AlreadySexyException;
import com.smartum.mpr.exception.AlreadyTypeException;
import com.smartum.mpr.exception.AuthorizationException;
import com.smartum.mpr.exception.DatabaseIntegrityException;
import com.smartum.mpr.exception.ErrorCodes;
import com.smartum.mpr.exception.IncorrectCredentionalsException;
import com.smartum.mpr.exception.NotCorrectPermission;
import com.smartum.mpr.exception.NotExistsCategoryException;
import com.smartum.mpr.exception.NotExistsImageException;
import com.smartum.mpr.exception.NotExistsItemException;
import com.smartum.mpr.exception.NotExistsJobException;
import com.smartum.mpr.exception.NotExistsLikeException;
import com.smartum.mpr.exception.NotExistsNewsException;
import com.smartum.mpr.exception.NotExistsSubCategoryException;
import com.smartum.mpr.exception.NotExistsTokenException;
import com.smartum.mpr.exception.NotExistsUserException;
import com.smartum.mpr.exception.NotExiststCountryException;
import com.smartum.mpr.exception.NotValidaParamException;
import com.smartum.mpr.exception.UnsupportedImageFormat;
import com.smartum.mpr.json.parsers.CountryParser;
import com.smartum.mpr.json.parsers.CountyParser;
import com.smartum.mpr.json.parsers.EntityParser;
import com.smartum.mpr.json.parsers.ImageParser;
import com.smartum.mpr.json.parsers.ItemCategoryParser;
import com.smartum.mpr.json.parsers.ItemImageParser;
import com.smartum.mpr.json.parsers.ItemParser;
import com.smartum.mpr.json.parsers.ItemSubCategoryParser;
import com.smartum.mpr.json.parsers.JobCategoryParser;
import com.smartum.mpr.json.parsers.JobParser;
import com.smartum.mpr.json.parsers.JobSubCategoryParser;
import com.smartum.mpr.json.parsers.NewsParser;
import com.smartum.mpr.json.parsers.StateParser;
import com.smartum.mpr.json.parsers.UploadedImageCommentParser;
import com.smartum.mpr.json.parsers.UploadedImageLikeParser;
import com.smartum.mpr.json.parsers.UploadedImageParser;
import com.smartum.mpr.json.parsers.UserParser;
import com.smartum.mpr.model.Client;
import com.smartum.mpr.model.Country;
import com.smartum.mpr.model.Image;
import com.smartum.mpr.model.Item;
import com.smartum.mpr.model.ItemCategory;
import com.smartum.mpr.model.ItemImage;
import com.smartum.mpr.model.ItemSubCategory;
import com.smartum.mpr.model.Job;
import com.smartum.mpr.model.JobCategory;
import com.smartum.mpr.model.JobSubCategory;
import com.smartum.mpr.model.News;
import com.smartum.mpr.model.State;
import com.smartum.mpr.model.UploadedImage;
import com.smartum.mpr.model.UploadedImageComment;
import com.smartum.mpr.model.UploadedImageLike;
import com.smartum.mpr.model.User;
import com.smartum.mpr.model.County;

public abstract class BaseService<T> {

	protected static final String TIMESTAMP_PROPERTY = "timestamp";
	protected static final String AMOUNT_PROPERTY = "amount";

	@Autowired
	ClientDao clientDao;

	@Autowired
	UserDao userDao;

	protected Logger LOGGER_API = Logger.getLogger("api");
	protected Logger LOGGER_COMMON = Logger.getLogger("api");

	protected ResponseEntity<String> handleException(Exception e) {
		ErrorCodes ec = ErrorCodes.INTERNAL_SERVER_ERROR;
		String msg = "";
		if (e != null) {
			try {
				throw e;
			} catch (JsonMappingException ex) {
				ec = ErrorCodes.GENERATE_JSON_RESPONSE_ERROR;
				msg = "mapping json exception: " + ex.getLocalizedMessage();
			} catch (IOException ex) {
				ec = ErrorCodes.GENERATE_JSON_RESPONSE_ERROR;
				msg = "io json exception: " + ex.getLocalizedMessage();
			} catch (AuthorizationException ex) {
				ec = ErrorCodes.NOT_VALID_CREDENTIONAL_ERROR;
				msg = "not valid credentionals: " + ex.getLocalizedMessage();
			} catch (DatabaseIntegrityException ex) {
				ec = ErrorCodes.DATABASE_INTEGRITY_ERROR;
				msg = "database integrity: " + ex.getLocalizedMessage();
			} catch (AlreadyExistsUserException ex) {
				ec = ErrorCodes.ALREADY_EXISTS_USER_ERROR;
				msg = ex.getLocalizedMessage();
			} catch (NotExistsTokenException ex) {
				ec = ErrorCodes.TOKEN_NOT_VALID;
				msg = "token not valid ";
			} catch (NotExistsImageException ex) {
				ec = ErrorCodes.NOT_EXISTS_IMAGE;
				msg = ex.getMessage();
			} catch (NotExistsLikeException ex) {
				ec = ErrorCodes.NOT_EXISTS_LIKE;
				msg = "not exists like";
			} catch (NotValidaParamException ex) {
				ec = ErrorCodes.NOT_VALID_PARAM;
				msg = "not valid para: " + ex.getLocalizedMessage();
			} catch (IncorrectCredentionalsException ex) {
				ec = ErrorCodes.INCORRECT_CREDENTIONALS;
				msg = "incorrect credentionals";
			} catch (NotExiststCountryException ex) {
				ec = ErrorCodes.NOT_EXISTS_COUNTRY;
				msg = "not exists country";
			} catch (AlreadyLikedException ex) {
				ec = ErrorCodes.ALREADY_LIKED;
				msg = "already liked: " + ex.getLocalizedMessage();
			} catch (AlreadySexyException ex) {
				ec = ErrorCodes.ALREADY_SEXY;
				msg = "already sexy: " + ex.getLocalizedMessage();
			} catch (AlreadyGangsterException ex) {
				ec = ErrorCodes.ALREADY_GANSTER;
				msg = "already gangster: " + ex.getLocalizedMessage();
			} catch (AlreadyClassyException ex) {
				ec = ErrorCodes.ALREADY_CLASSY;
				msg = "already classy: " + ex.getLocalizedMessage();
			} catch (AlreadyOddException ex) {
				ec = ErrorCodes.ALREADY_ODD;
				msg = "already odd: " + ex.getLocalizedMessage();
			} catch (AlreadyHoaxException ex) {
				ec = ErrorCodes.ALREADY_HOAX;
				msg = "already hoax: " + ex.getLocalizedMessage();
			} catch (AlreadyTypeException ex) {
				ec = ErrorCodes.ALREADY_TYPED;
				msg = "already typed: " + ex.getLocalizedMessage();
			} catch (NotExistsItemException ex) {
				ec = ErrorCodes.NOT_EXISTS_ITEM;
				msg = "not exists item " + ex.getLocalizedMessage();
			} catch (NotExistsJobException ex) {
				ec = ErrorCodes.NOT_EXISTS_JOB;
				msg = "not exists job " + ex.getLocalizedMessage();
			} catch (NotExistsSubCategoryException ex) {
				ec = ErrorCodes.NOT_EXISTS_SUBCATEGORY;
				msg = "not exists subcategory";
			} catch (NotCorrectPermission ex) {
				ec = ErrorCodes.NOT_CORRECT_PERMISSION;
				msg = ex.getMessage();
			} catch (NotExistsUserException ex) {
				ec = ErrorCodes.NOT_EXISTS_USER;
				msg = "not exists user" + ex.getLocalizedMessage();
			} catch (NotExistsNewsException ex) {
				ec = ErrorCodes.NOT_EXISTS_NEWS;
				msg = "not exists news";
			} catch (UnsupportedImageFormat ex) {
				ec = ErrorCodes.NOT_SUPPORTED_IMAGE_FORMAT;
				msg = "not supported image format";
			}catch (NotExistsCategoryException ex) {
				ec = ErrorCodes.NOT_EXISTS_CATEGORY;
				msg = "not exists category";
			} catch (Exception ex) {
				ec = ErrorCodes.INTERNAL_SERVER_ERROR;
				msg = "Unknow error: " + ex.getLocalizedMessage();
			}
		}
		return getResponseInJsonType(generateErrorJson(ec, msg), HttpStatus.INTERNAL_SERVER_ERROR);
	}

	protected <T> ResponseEntity<T> getResponseInJsonType(T entity, HttpStatus status) {
		HttpHeaders responseHeaders = new HttpHeaders();
		responseHeaders.setContentType(MediaType.APPLICATION_JSON);

		return new ResponseEntity<T>(entity, responseHeaders, status);
	}

	protected String generateErrorJson(ErrorCodes code, String message) {
		return " {  \"error\":{ \"error_code\":\"" + code.getCode() + "\", \"error_message\": \"" + message + " \"}}";
	}

	protected String generateSuccessJson(String jsonResponse) {
		return " {\"response\": " + jsonResponse + " }";
	}

	protected <K> EntityParser<K> getParserByEntityClass(Class<K> classObject) throws Exception {
		if (classObject.equals(User.class)) {
			return (EntityParser<K>) new UserParser();
		} else if (classObject.equals(Item.class)) {
			return (EntityParser<K>) new ItemParser();
		} else if (classObject.equals(Job.class)) {
			return (EntityParser<K>) new JobParser();
		} else if (classObject.equals(News.class)) {
			return (EntityParser<K>) new NewsParser();
		} else if (classObject.equals(Image.class)) {
			return (EntityParser<K>) new ImageParser();
		} else if (classObject.equals(UploadedImage.class)) {
			return (EntityParser<K>) new UploadedImageParser();
		} else if (classObject.equals(UploadedImageLike.class)) {
			return (EntityParser<K>) new UploadedImageLikeParser();
		} else if (classObject.equals(ItemCategory.class)) {
			return (EntityParser<K>) new ItemCategoryParser();
		} else if (classObject.equals(ItemSubCategory.class)) {
			return (EntityParser<K>) new ItemSubCategoryParser();
		} else if (classObject.equals(JobCategory.class)) {
			return (EntityParser<K>) new JobCategoryParser();
		} else if (classObject.equals(ItemImage.class)) {
			return (EntityParser<K>) new ItemImageParser();
		} else if (classObject.equals(JobSubCategory.class)) {
			return (EntityParser<K>) new JobSubCategoryParser();
		} else if (classObject.equals(State.class)) {
			return (EntityParser<K>) new StateParser();
		} else if (classObject.equals(Country.class)) {
			return (EntityParser<K>) new CountryParser();
		} else if (classObject.equals(County.class)) {
			return (EntityParser<K>) new CountyParser();
		} else if (classObject.equals(UploadedImageComment.class)) {
			return (EntityParser<K>) new UploadedImageCommentParser();
		}
		throw new Exception("can't find entity parser");
	}

	protected void logResponse(ResponseEntity<String> response) {
		LOGGER_API.info("Body: " + response.getBody());
		LOGGER_API.info("Status code: " + response.getStatusCode().toString());
		LOGGER_API.info("Headers");
		for (String key : response.getHeaders().keySet()) {
			LOGGER_API.info("Header - " + key + ":" + response.getHeaders().get(key));
		}
	}

	protected String extractRequestStringData(String json) throws Exception {
		ObjectMapper mapper = getTargetParser().getMapper();
		JsonNode rootNode = mapper.readTree(json);
		return mapper.writeValueAsString(rootNode.get("request"));
	}

	protected String extractTokenStringData(String json) throws Exception {
		ObjectMapper mapper = getTargetParser().getMapper();
		JsonNode rootNode = mapper.readTree(json);
		return rootNode.get("auth_token").asText();
	}

	protected String extractClientSecretStringData(String json) throws Exception {
		ObjectMapper mapper = getTargetParser().getMapper();
		JsonNode rootNode = mapper.readTree(json);
		return rootNode.get("client_secret").asText();
	}

	protected String extractClientIDStringData(String json) throws Exception {
		ObjectMapper mapper = getTargetParser().getMapper();
		JsonNode rootNode = mapper.readTree(json);
		return rootNode.get("client_id").asText();
	}

	protected void checkRequestPermissions(String request) throws IncorrectCredentionalsException {
		boolean hasPermission = false;
		try {
			LOGGER_API.info("doing checkRequestPermissions 1");
			String token = extractTokenStringData(request);
			LOGGER_API.info("Recupero token");
			userDao.findUserByToken(token);
			LOGGER_API.info("no te caiste jum!!");
			hasPermission = true;
		} catch (Exception e) {
			LOGGER_API.info("Error checkRequestPermissions 1:" + e.toString());
			LOGGER_API.info("check permission: no auth_token in request json or it's invalid");
			try {
				System.out.println("doing checkRequestPermissions 2");
				String clientID = extractClientIDStringData(request);
				String secret = extractClientSecretStringData(request);
				Client client = clientDao.get(Long.parseLong(clientID));
				if (client != null) {
					if (client.getSecret().equals(secret)) {
						hasPermission = true;
					}
				}
			} catch (Exception ex) {
				System.out.println("Error checkRequestPermissions 2:" + e.toString());
				LOGGER_API.info(ex.getLocalizedMessage());
				LOGGER_API.info("check permission: no client_id or client_secret in request json or they are invalid");
			}
		}

		if (!hasPermission) {
			System.out.println("Error checkRequestPermissions 3:");
			LOGGER_API.info("check permission: don't have permission");
			throw new IncorrectCredentionalsException();
		}
	}

	protected EntityParser<T> getTargetParser() throws Exception {
		return getParserByEntityClass(getEntityClass());
	}

	protected abstract Class<T> getEntityClass();

}
